home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 11 / Mac Magazin and MacEasy Magazine CD - Issue 11.iso / Sharewarebibliothek / Entwickler / WASTE 1.1b1 Distribution / WASTE Source / WEMouse.p < prev    next >
Text File  |  1995-06-01  |  32KB  |  1,172 lines

  1. unit WEMouse;
  2.  
  3. { WASTE PROJECT }
  4. { Mouse Clicks and support for Drag and Drop }
  5.  
  6. { Copyright © 1993-1995 Marco Piovanelli }
  7. { All Rights Reserved }
  8.  
  9. interface
  10.     uses
  11.         WEBirthDeath;
  12.  
  13.     procedure WEClick (mouseLoc: Point;
  14.                                     modifiers: Integer;
  15.                                     clickTime: LongInt;
  16.                                     hWE: WEHandle);
  17.     function WETrackDrag (theMessage: DragTrackingMessage;
  18.                                     theDrag: DragReference;
  19.                                     hWE: WEHandle): OSErr;
  20.     function WEReceiveDrag (theDrag: DragReference;
  21.                                     hWE: WEHandle): OSErr;
  22.  
  23.     function WECanAcceptDrag (theDrag: DragReference;
  24.                                     hWE: WEHandle): Boolean;
  25.     function WEDraggedToTrash (theDrag: DragReference): Boolean;
  26.  
  27. implementation
  28.     uses
  29.         Folders, WEScraps;
  30.  
  31.     const
  32.  
  33.         noDragErr = 128;
  34.         kTextMargin = 3;                        { width of border area surrounding the text (in pixels) }
  35.         kAutoScrollDelay = 10;                { delay before auto-scroll starts (in ticks) }
  36.  
  37. {$IFC WASTE_SEGMENT}
  38. {$S WASTE_DRAG}
  39. {$ENDC}
  40.  
  41.     var
  42.  
  43. { static variables }
  44.  
  45.         _weFlavorSender: DragSendDataUPP;
  46.  
  47.     function _WEIsOptionDrag (theDrag: DragReference): Boolean;
  48.         var
  49.             modifiers, downModifiers, upModifiers: Integer;
  50.     begin
  51.  
  52. { get drag modifiers }
  53.         if (GetDragModifiers(theDrag, modifiers, downModifiers, upModifiers) <> noErr) then
  54.             ;
  55.  
  56. { return TRUE if the option key was held down at the beginning and/or at the end }
  57.         _WEIsOptionDrag := BAND(BOR(downModifiers, upModifiers), optionKey) <> 0;
  58.  
  59.     end;  { _WEIsOptionDrag }
  60.  
  61.     function _WEGetFlavor (theDrag: DragReference;
  62.                                     theItem: ItemReference;
  63.                                     requestedType: FlavorType;
  64.                                     hFlavor: Handle;
  65.                                     translateDragHook: ProcPtr): OSErr;
  66.  
  67. { get the requested flavor out of the specified drag reference and put it into }
  68. { the given handle, if any -- if hFlavor is NIL, just check whether the specified flavor }
  69. { is there or can be obtained by invoking a user-defined translation routine }
  70.  
  71.         label
  72.             1;
  73.         var
  74.             theFlags: FlavorFlags;
  75.             theSize: Size;
  76.             saveFlavorLock: Boolean;
  77.             err: OSErr;
  78.     begin
  79.  
  80. { see if the drag item has the requested flavor type, }
  81. { without forcing the actual data to be sent and/or translated }
  82.         err := GetFlavorFlags(theDrag, theItem, requestedType, theFlags);
  83.         if (err = badDragFlavorErr) then
  84.             begin
  85.  
  86. { requested flavor is not available: our client may try a custom translation }
  87. { this is especially handy to translate HFS objects like TEXT and PICT files }
  88.                 if (translateDragHook <> nil) then
  89.                     err := CallWETranslateDragProc(theDrag, theItem, requestedType, hFlavor, translateDragHook);
  90.             end
  91.         else if (err = noErr) then
  92.             begin
  93.  
  94. { requested flavor is available: get it if hFlavor is not NIL }
  95.                 if (hFlavor = nil) then
  96.                     goto 1;
  97.  
  98. { get size of flavor data }
  99.                 err := GetFlavorDataSize(theDrag, theItem, requestedType, theSize);
  100.                 if (err <> noErr) then
  101.                     goto 1;
  102.  
  103. { resize the handle }
  104.                 SetHandleSize(hFlavor, theSize);
  105.                 err := MemError;
  106.                 if (err <> noErr) then
  107.                     goto 1;
  108.  
  109. { get flavor data }
  110.                 saveFlavorLock := _WESetHandleLock(hFlavor, true);
  111.                 err := GetFlavorData(theDrag, theItem, requestedType, hFlavor^, theSize, 0);
  112.                 if (_WESetHandleLock(hFlavor, saveFlavorLock)) then
  113.                     ;
  114.             end;
  115.  
  116. 1:
  117. { return result code }
  118.         _WEGetFlavor := err;
  119.  
  120.     end;  { _WEGetFlavor }
  121.  
  122.     function _WEExtractFlavor (theDrag: DragReference;
  123.                                     theItem: ItemReference;
  124.                                     theType: FlavorType;
  125.                                     var hFlavor: Handle;
  126.                                     translateDragHook: ProcPtr): OSErr;
  127.  
  128. { _WEExtractFlavor creates a new handle and calls _WEGetFlavor on it }
  129.  
  130.         var
  131.             err: OSErr;
  132.     begin
  133.  
  134. { allocate a new handle }
  135.         err := _WEAllocate(0, kAllocTemp, hFlavor);
  136.         if (err = noErr) then
  137.             begin
  138.  
  139. { put the requested flavor into this handle }
  140.                 err := _WEGetFlavor(theDrag, theItem, theType, hFlavor, translateDragHook);
  141.  
  142. { if an error occurred, forget the handle }
  143.                 if (err <> noErr) then
  144.                     _WEForgetHandle(hFlavor);
  145.             end;
  146.  
  147.         _WEExtractFlavor := err;
  148.     end;  { _WEExtractFlavor }
  149.  
  150.     function WECanAcceptDrag (theDrag: DragReference;
  151.                                     hWE: WEHandle): Boolean;
  152.         label
  153.             1;
  154.         var
  155.             pWE: WEPtr;
  156.             dragItemIndex, numDragItems: Integer;
  157.             theItem: ItemReference;
  158.             objectIndex: Integer;
  159.             objectType: OSType;
  160.             saveWELock: Boolean;
  161.             err: OSErr;
  162.     begin
  163.         WECanAcceptDrag := false;        { assume drag can't be accepted }
  164.  
  165. { lock the WE record }
  166.         saveWELock := _WESetHandleLock(hWE, true);
  167.         pWE := hWE^;
  168.  
  169. { refuse all drags if the weFReadOnly flag is set }
  170.         if (BTST(pWE^.flags, weFReadOnly)) then
  171.             goto 1;
  172.  
  173. { count items in theDrag }
  174.         if (CountDragItems(theDrag, numDragItems) <> noErr) then
  175.             goto 1;
  176.  
  177.         for dragItemIndex := 1 to numDragItems do
  178.             begin
  179.  
  180. { get item reference number for current drag item }
  181.                 if (GetDragItemReferenceNumber(theDrag, dragItemIndex, theItem) <> noErr) then
  182.                     goto 1;
  183.  
  184. { see if this drag item contains a text flavor }
  185.                 err := _WEGetFlavor(theDrag, theItem, kTypeText, nil, pWE^.translateDragHook);
  186.                 if (err = badDragFlavorErr) then
  187.                     begin
  188.  
  189. { see if this drag item contains a flavor matching one of the registered object types }
  190.                         objectIndex := 0;
  191.                         while (_WEGetIndObjectType(objectIndex, objectType, hWE) = noErr) do
  192.                             begin
  193.                                 err := _WEGetFlavor(theDrag, theItem, objectType, nil, pWE^.translateDragHook);
  194.                                 if (err <> badDragFlavorErr) then
  195.                                     Leave;  { enclosing while }
  196.                                 objectIndex := objectIndex + 1;
  197.                             end;  { while }
  198.                     end;
  199.  
  200.                 if (err <> noErr) then
  201.                     goto 1;
  202.  
  203.             end;  { for }
  204.  
  205.         WECanAcceptDrag := true;
  206.  
  207. 1:
  208. { unlock the WE record }
  209.         if (_WESetHandleLock(hWE, saveWELock)) then
  210.             ;
  211.  
  212.     end;  { WECanAcceptDrag }
  213.  
  214.     procedure _WEUpdateDragCaret (offset: LongInt;
  215.                                     hWE: WEHandle);
  216.         var
  217.             ticks: LongInt;
  218.             pWE: WEPtr;
  219.     begin
  220.  
  221. { the WE record must be already locked }
  222.         pWE := hWE^;
  223.  
  224. { get current time }
  225.         ticks := TickCount;
  226.  
  227.         if (offset = pWE^.dragCaretOffset) then
  228.             begin
  229.  
  230. { drag caret offset didn't change; blink the caret }
  231.                 if (GetCaretTime < ticks - pWE^.caretTime) and (offset <> kInvalidOffset) then
  232.                     begin
  233.                         _WEDrawCaret(pWE^.dragCaretOffset, hWE);
  234.                         pWE^.flags := BXOR(pWE^.flags, BSL(1, weFDragCaretVisible));
  235.                         pWE^.caretTime := ticks;
  236.                     end;
  237.             end
  238.         else
  239.             begin
  240.  
  241. { drag caret offset did change }
  242. { hide old caret, if it's showing }
  243.                 if BTST(pWE^.flags, weFDragCaretVisible) then
  244.                     _WEDrawCaret(pWE^.dragCaretOffset, hWE);
  245.  
  246. { show new caret (unless offset is kInvalidOffset) }
  247.                 if (offset <> kInvalidOffset) then
  248.                     begin
  249.                         _WEDrawCaret(offset, hWE);
  250.                         BSET(pWE^.flags, weFDragCaretVisible);
  251.                         pWE^.caretTime := ticks;
  252.                     end
  253.                 else
  254.                     BCLR(pWE^.flags, weFDragCaretVisible);
  255.  
  256. { remember drag caret offset }
  257.                 pWE^.dragCaretOffset := offset;
  258.             end;
  259.     end;  { _WEUpdateDragCaret }
  260.  
  261.     function WETrackDrag (theMessage: DragTrackingMessage;
  262.                                     theDrag: DragReference;
  263.                                     hWE: WEHandle): OSErr;
  264.         label
  265.             1;
  266.         var
  267.             pWE: WEPtr;
  268.             attributes: DragAttributes;
  269.             mouse: Point;
  270.             tmpRgn: RgnHandle;
  271.             thePoint: LongPt;
  272.             offset, ticks: LongInt;
  273.             edge: SignedByte;
  274.             saveWELock: Boolean;
  275.             err: OSErr;
  276.     begin
  277.  
  278. { lock the WE record }
  279.         saveWELock := _WESetHandleLock(hWE, true);
  280.         pWE := hWE^;
  281.  
  282. { dispatch on theMessage }
  283.         case theMessage of
  284.  
  285.             dragTrackingEnterWindow: 
  286.                 begin
  287.  
  288. { determine whether we can accept this drag }
  289.                     if (WECanAcceptDrag(theDrag, hWE)) then
  290.                         BSET(pWE^.flags, weFCanAcceptDrag)
  291.                     else
  292.                         BCLR(pWE^.flags, weFCanAcceptDrag);
  293.  
  294. { reset clickTime }
  295.                     pWE^.clickTime := 0;
  296.  
  297.                 end;
  298.  
  299.             dragTrackingInWindow: 
  300.                 if BTST(pWE^.flags, weFCanAcceptDrag) then
  301.                     begin
  302.  
  303. { get drag attributes }
  304.                         err := GetDragAttributes(theDrag, attributes);
  305.                         if (err <> noErr) then
  306.                             goto 1;
  307.  
  308. { get current mouse location in local coordinates }
  309.                         err := GetDragMouse(theDrag, mouse, PointPtr(0)^);
  310.                         if (err <> noErr) then
  311.                             goto 1;
  312.                         GlobalToLocal(mouse);
  313.  
  314.                         if (PtInRgn(mouse, pWE^.viewRgn)) then
  315.                             begin
  316.  
  317. { mouse is in text area }
  318. { hilite the text rectangle, if we haven't already }
  319. { and if the drag has left sender window since drag tracking started }
  320.                                 if ((not BTST(pWE^.flags, weFHilited)) and (BAND(attributes, dragHasLeftSenderWindow) <> 0)) then
  321.                                     begin
  322.                                         tmpRgn := NewRgn;
  323.                                         CopyRgn(pWE^.viewRgn, tmpRgn);
  324.                                         InsetRgn(tmpRgn, -kTextMargin, -kTextMargin);
  325.                                         if (ShowDragHilite(theDrag, tmpRgn, true) <> noErr) then
  326.                                             ;
  327.                                         DisposeRgn(tmpRgn);
  328.                                         BSET(pWE^.flags, weFHilited);
  329.                                     end;
  330.  
  331. { hide the caret }
  332.                                 if BTST(pWE^.flags, weFCaretVisible) then
  333.                                     _WEBlinkCaret(hWE);
  334.  
  335. { get text offset corresponding to mouse location }
  336.                                 WEPointToLongPoint(mouse, thePoint);
  337.                                 offset := WEGetOffset(thePoint, edge, hWE);
  338.  
  339. { if offset is within the original selection range, don't display drag feedback }
  340.                                 if (theDrag = pWE^.currentDrag) then
  341.                                     if (_WEOffsetInRange(offset, edge, pWE^.selStart, pWE^.selEnd)) then
  342.                                         offset := kInvalidOffset;
  343.  
  344. { provide a drag feedback in the form of a blinking caret }
  345.                                 _WEUpdateDragCaret(offset, hWE);
  346.  
  347. { clear clickTime }
  348.                                 pWE^.clickTime := 0;
  349.  
  350.                             end
  351.                         else
  352.                             begin
  353.  
  354. { mouse is outside text area }
  355. { dehilite the text rectangle, if it's hilited }
  356.                                 if (BTST(pWE^.flags, weFHilited)) then
  357.                                     begin
  358.                                         if (HideDragHilite(theDrag) <> noErr) then
  359.                                             ;
  360.                                         BCLR(pWE^.flags, weFHilited);
  361.                                     end;
  362.  
  363. { hide the drag caret, if it's showing }
  364.                                 _WEUpdateDragCaret(kInvalidOffset, hWE);
  365.  
  366. { if the mouse has been remaining outside the view region for 10 ticks or more }
  367. { and this drag was created by this WE instance, call the click loop routine }
  368.                                 if (theDrag = pWE^.currentDrag) then
  369.                                     begin
  370.                                         ticks := TickCount;
  371.                                         if (pWE^.clickTime = 0) then
  372.                                             pWE^.clickTime := ticks
  373.                                         else if (ticks > pWE^.clickTime + kAutoScrollDelay) then
  374.                                             if (pWE^.clickLoop <> nil) then
  375.                                                 if (CallWEClickLoopProc(hWE, pWE^.clickLoop)) then
  376.                                                     ;
  377.                                     end;
  378.                             end;
  379.                     end;  { case dragTrackingInWindow }
  380.  
  381.             dragTrackingLeaveWindow: 
  382.                 begin
  383.  
  384. { drag has left this window }
  385. { dehilite the text area if necessary }
  386.                     if (BTST(pWE^.flags, weFHilited)) then
  387.                         begin
  388.                             if (HideDragHilite(theDrag) <> noErr) then
  389.                                 ;
  390.                             BCLR(pWE^.flags, weFHilited);
  391.                         end;
  392.  
  393. { hide the drag caret, if it's showing }
  394.                     _WEUpdateDragCaret(kInvalidOffset, hWE);
  395.  
  396.                 end;
  397.  
  398.             otherwise
  399.                 ;
  400.         end;  { case theMessage }
  401.  
  402. { clear result code }
  403.         err := noErr;
  404.  
  405. 1:
  406. { return result code }
  407.         WETrackDrag := err;
  408.  
  409. { unlock the WE record }
  410.         if (_WESetHandleLock(hWE, saveWELock)) then
  411.             ;
  412.  
  413.     end;  { WETrackDrag }
  414.  
  415.     function WEReceiveDrag (theDrag: DragReference;
  416.                                     hWE: WEHandle): OSErr;
  417.         label
  418.             1;
  419.         var
  420.             pWE: WEPtr;
  421.             hAction: WEActionHandle;
  422.             mouse: Point;
  423.             dropLocation: LongPt;
  424.             insertionOffset, insertionLength: LongInt;
  425.             sourceStart, sourceEnd: LongInt;
  426.             destStart, destEnd: LongInt;
  427.             delta: LongInt;
  428.             dragItemIndex, numDragItems: Integer;
  429.             theItem: ItemReference;
  430.             hText, hStyles, hSoup, hObjectData: Handle;
  431.             objectIndex: Integer;
  432.             objectType: OSType;
  433.             savePort: GrafPtr;
  434.             intPasteAction: Integer;
  435.             saveUndoSupport, saveInhibitRecal: Integer;
  436.             dropEdge, space: SignedByte;
  437.             isMove, isBackwards: Boolean;
  438.             saveWELock: Boolean;
  439.             err: OSErr;
  440.     begin
  441.         isMove := false;
  442.         hText := nil;
  443.         hStyles := nil;
  444.         hSoup := nil;
  445.         hObjectData := nil;
  446.  
  447. { lock the WE record }
  448.         saveWELock := _WESetHandleLock(hWE, true);
  449.         pWE := hWE^;
  450.  
  451. { set up the port }
  452.         GetPort(savePort);
  453.         SetPort(pWE^.port);
  454.  
  455. { stop any ongoing inline input session }
  456.         WEStopInlineSession(hWE);
  457.  
  458. { hide the drag caret }
  459.         _WEUpdateDragCaret(kInvalidOffset, hWE);
  460.  
  461. { refuse this drag if it doesn't taste good }
  462.         err := badDragFlavorErr;
  463.         if (WECanAcceptDrag(theDrag, hWE) = false) then
  464.             goto 1;
  465.  
  466. { get drop location in local coordinates }
  467.         err := GetDragMouse(theDrag, mouse, PointPtr(0)^);
  468.         if (err <> noErr) then
  469.             goto 1;
  470.         GlobalToLocal(mouse);
  471.  
  472. { for the drag to be accepted, the drop location must be within the view region }
  473.         err := dragNotAcceptedErr;
  474.         if (PtInRgn(mouse, pWE^.viewRgn) = false) then
  475.             goto 1;
  476.  
  477. { get drop offset into the text }
  478.         WEPointToLongPoint(mouse, dropLocation);
  479.         insertionOffset := WEGetOffset(dropLocation, dropEdge, hWE);
  480.  
  481. { destStart/destEnd define the range to highlight at the end of the drag }
  482.         destStart := insertionOffset;
  483.  
  484. { drag originated from this same window? }
  485.         if (theDrag = pWE^.currentDrag) then
  486.             begin
  487.  
  488. { sourceStart/sourceEnd define the range to delete at the end of the move }
  489.                 sourceStart := pWE^.selStart;
  490.                 sourceEnd := pWE^.selEnd;
  491.  
  492. { remember text length before insertion }
  493.                 delta := pWE^.textLength;
  494.  
  495. { if insertion offset is within the original selection range, abort the drag }
  496. (*err := dragNotAcceptedErr;*)
  497.                 if (_WEOffsetInRange(insertionOffset, dropEdge, sourceStart, sourceEnd)) then
  498.                     goto 1;
  499.  
  500. { if the drag originated from this window, a move, }
  501. { rather than a copy, should be performed }
  502. { Exception: the option key may be held down at mouse-down }
  503. { or mouse-up time to force a copy operation. }
  504.  
  505.                 isMove := (_WEIsOptionDrag(theDrag) = false);
  506.                 isBackwards := (insertionOffset <= sourceStart);
  507.             end;  { if intra-window drag }
  508.  
  509. { clear null style }
  510.         BCLR(pWE^.flags, weFUseNullStyle);
  511.  
  512. { hide selection highlighting }
  513.         _WEHiliteRange(pWE^.selStart, pWE^.selEnd, hWE);
  514.  
  515. { increment modification count }
  516.         pWE^.modCount := pWE^.modCount + 1;
  517.  
  518. { if undo support is enabled, create a new action so we'll be able to undo the insertion }
  519.         if (BTST(pWE^.flags, weFUndoSupport)) then
  520.             begin
  521.                 WEClearUndo(hWE);
  522.                 if (WENewAction(insertionOffset, insertionOffset, 0, weAKDrag, 0, hWE, hAction) = noErr) then
  523.                     if (WEPushAction(hAction) <> noErr) then
  524.                         ;
  525.             end;
  526.  
  527. { count items in this drag }
  528.         err := CountDragItems(theDrag, numDragItems);
  529.         if (err <> noErr) then
  530.             goto 1;
  531.  
  532.         for dragItemIndex := 1 to numDragItems do
  533.             begin
  534.  
  535. { get item reference number for current drag item }
  536.                 err := GetDragItemReferenceNumber(theDrag, dragItemIndex, theItem);
  537.                 if (err <> noErr) then
  538.                     goto 1;
  539.  
  540. { see if this drag item contains a text flavor }
  541.                 err := _WEExtractFlavor(theDrag, theItem, kTypeText, hText, pWE^.translateDragHook);
  542.                 if (err = noErr) then
  543.                     begin
  544.  
  545. { extract accompanying styles and soup, if any }
  546.                         err := _WEExtractFlavor(theDrag, theItem, kTypeStyles, hStyles, pWE^.translateDragHook);
  547.                         if (err <> noErr) and (err <> badDragFlavorErr) then
  548.                             goto 1;
  549.                         err := _WEExtractFlavor(theDrag, theItem, kTypeSoup, hSoup, pWE^.translateDragHook);
  550.                         if (err <> noErr) and (err <> badDragFlavorErr) then
  551.                             goto 1;
  552.  
  553. { any extra space added because of intelligent cut-and-paste rules will use the }
  554. { style attributes set at the insertion point }
  555.                         if (dragItemIndex = 1) then
  556.                             begin
  557.                                 pWE^.selStart := insertionOffset;
  558.                                 pWE^.selEnd := insertionOffset;
  559.                                 _WESynchNullStyle(hWE);
  560.                             end;
  561.  
  562. { get text length }
  563.                         insertionLength := GetHandleSize(hText);
  564.                         destEnd := insertionOffset + insertionLength;
  565.  
  566. { insert the new text at the insertion point }
  567.                         HLock(hText);
  568.                         err := _WEInsertText(insertionOffset, hText^, insertionLength, hWE);
  569.                         _WEForgetHandle(hText);
  570.                         if (err <> noErr) then
  571.                             goto 1;
  572.  
  573. { adjust deletion range length in undo buffer }
  574.                         _WEAdjustUndoRange(insertionLength, hWE);
  575.  
  576. { apply the accompanying styles, if any }
  577.                         if (hStyles <> nil) then
  578.                             begin
  579.                                 err := _WEApplyStyleScrap(insertionOffset, destEnd, StScrpHandle(hStyles), hWE);
  580.                                 if (err <> noErr) then
  581.                                     goto 1;
  582.                                 _WEForgetHandle(hStyles);
  583.                             end;
  584.  
  585. { apply the accompanying soup, if any }
  586.                         if (hSoup <> nil) then
  587.                             begin
  588.                                 err := _WEApplySoup(insertionOffset, hSoup, hWE);
  589.                                 if (err <> noErr) then
  590.                                     goto 1;
  591.                                 _WEForgetHandle(hSoup);
  592.                             end;
  593.  
  594. { determine whether an extra space should be added before or after the inserted text }
  595.                         intPasteAction := _WEIntelligentPaste(insertionOffset, destEnd, hWE);
  596.  
  597. { add the extra space, if necessary }
  598.                         if (intPasteAction <> weDontAddSpaces) then
  599.                             begin
  600.  
  601.                                 space := kSpace;
  602.                                 if (intPasteAction = weAddSpaceOnLeftSide) then
  603.                                     begin
  604.                                         err := _WEInsertText(insertionOffset, @space, 1, hWE);
  605.                                         if (err <> noErr) then
  606.                                             goto 1;
  607.  
  608.                                         destEnd := destEnd + 1;
  609.  
  610. { if an extra space is inserted in front of all dropped items, }
  611. { don't count it when eventually highlighting the destination range }
  612.                                         if (dragItemIndex = 1) then
  613.                                             destStart := destStart + 1;
  614.  
  615.                                     end
  616.                                 else
  617.                                     begin
  618.                                         err := _WEInsertText(destEnd, @space, 1, hWE);
  619.                                         if (err <> noErr) then
  620.                                             goto 1;
  621.                                     end;
  622.  
  623.                                 insertionLength := insertionLength + 1;
  624.                                 _WEAdjustUndoRange(1, hWE);
  625.                             end;  { if extra space }
  626.  
  627.                     end
  628.                 else if (err = badDragFlavorErr) then
  629.                     begin
  630.  
  631. { no text flavor: there must be a flavor matching one of the registered object types }
  632.                         objectIndex := 0;
  633.                         while (_WEGetIndObjectType(objectIndex, objectType, hWE) = noErr) do
  634.                             begin
  635.                                 err := _WEExtractFlavor(theDrag, theItem, objectType, hObjectData, pWE^.translateDragHook);
  636.                                 if (err = noErr) then
  637.                                     Leave;  { enclosing while }
  638.                                 if (err <> badDragFlavorErr) then
  639.                                     goto 1;
  640.                                 objectIndex := objectIndex + 1;
  641.                             end;  { while }
  642.  
  643.                         if (err <> noErr) then
  644.                             goto 1;
  645.  
  646. { set insertion point on first iteration (*after* extracting flavors, in case we are }
  647. { doing an intra-window move, otherwise our send proc would be confused) }
  648.                         if (dragItemIndex = 1) then
  649.                             begin
  650.                                 pWE^.selStart := insertionOffset;
  651.                                 pWE^.selEnd := insertionOffset;
  652.                             end;
  653.  
  654. { insert the object, but without touching undo or redrawing the text }
  655.                         saveUndoSupport := WEFeatureFlag(weFUndoSupport, weBitClear, hWE);
  656.                         saveInhibitRecal := WEFeatureFlag(weFInhibitRecal, weBitSet, hWE);
  657.                         err := WEInsertObject(objectType, hObjectData, Point(0), hWE);
  658.                         saveUndoSupport := WEFeatureFlag(weFUndoSupport, saveUndoSupport, hWE);
  659.                         saveInhibitRecal := WEFeatureFlag(weFInhibitRecal, saveInhibitRecal, hWE);
  660.                         if (err <> noErr) then
  661.                             goto 1;
  662.  
  663.                         insertionLength := 1;
  664.                         destEnd := insertionOffset + 1;
  665.                         pWE^.modCount := pWE^.modCount - 1;        { compensate for increment made by WEInsertObject }
  666.                         _WEAdjustUndoRange(1, hWE);
  667.                     end
  668.                 else
  669.                     goto 1;
  670.  
  671. { advance insertion offset for subsequent drag items, if any }
  672.                 insertionOffset := insertionOffset + insertionLength;
  673.  
  674.             end;  { for }
  675.  
  676.         if (isMove) then
  677.             begin
  678.  
  679. { adjust source range }
  680.                 if (isBackwards) then
  681.                     begin
  682.                         delta := delta - pWE^.textLength;
  683.                         sourceStart := sourceStart - delta;
  684.                         sourceEnd := sourceEnd - delta;
  685.                     end;
  686.  
  687. { extend range according to intelligent cut-and-paste rules }
  688.                 _WEIntelligentCut(sourceStart, sourceEnd, hWE);
  689.  
  690. { if undo support is enabled, create a new action so we'll be able to undo the deletion }
  691.                 if (BTST(pWE^.flags, weFUndoSupport)) then
  692.                     if (WENewAction(sourceStart, sourceEnd, 0, weAKDrag, 0, hWE, hAction) = noErr) then
  693.                         if (WEPushAction(hAction) <> noErr) then
  694.                             ;
  695.  
  696. { delete source range }
  697.                 delta := pWE^.textLength;
  698.                 err := _WEDeleteRange(sourceStart, sourceEnd, hWE);
  699.                 if (err <> noErr) then
  700.                     goto 1;
  701.  
  702. { adjust destination range }
  703.                 if (isBackwards = false) then
  704.                     begin
  705.                         delta := delta - pWE^.textLength;
  706.                         destStart := destStart - delta;
  707.                         destEnd := destEnd - delta;
  708.                     end;
  709.  
  710.             end;  { if isMove }
  711.  
  712. { select the range encompassing all items dropped }
  713.         pWE^.selStart := destStart;
  714.         pWE^.selEnd := destEnd;
  715.  
  716. { redraw the text }
  717.         if (isMove) then
  718.             if (sourceStart < destStart) then
  719.                 err := _WERedraw(sourceStart, destEnd, hWE)
  720.             else
  721.                 err := _WERedraw(destStart, sourceEnd, hWE)
  722.         else
  723.             err := _WERedraw(destStart, destEnd, hWE);
  724.  
  725. 1:
  726. { return result code }
  727.         WEReceiveDrag := err;
  728.  
  729. { dispose of temporary handles }
  730.         _WEForgetHandle(hText);
  731.         _WEForgetHandle(hStyles);
  732.         _WEForgetHandle(hSoup);
  733.  
  734. { restore the port }
  735.         SetPort(savePort);
  736.  
  737. { unlock the WE record }
  738.         if (_WESetHandleLock(hWE, saveWELock)) then
  739.             ;
  740.  
  741.     end;  { WEReceiveDrag }
  742.  
  743.     function _WESendFlavor (theType: FlavorType;
  744.                                     dragSendRefCon: Ptr;
  745.                                     hWE: WEHandle;
  746.                                     theDrag: DragReference): OSErr;
  747.         label
  748.             1;
  749.         var
  750.             pWE: WEPtr;
  751.             selStart, selEnd: LongInt;
  752.             hObjectDesc: WEObjectDescHandle;
  753.             hItem: Handle;
  754.             disposeItem: Boolean;
  755.             err: OSErr;
  756.     begin
  757.         pWE := hWE^;
  758.         selStart := pWE^.selStart;
  759.         selEnd := pWE^.selEnd;
  760.         disposeItem := false;
  761.         hItem := nil;
  762.  
  763. { see if the selection contains an embedded object whose type matches the flavor type }
  764.         if (WEGetSelectedObject(hObjectDesc, hWE) = noErr) & (hObjectDesc^^.objectType = theType) then
  765.             hItem := hObjectDesc^^.objectDataHandle
  766.         else
  767.             begin
  768.  
  769. { allocate a temporary handle to hold a copy of the requested flavor }
  770.                 err := _WEAllocate(0, kAllocTemp, hItem);
  771.                 if (err <> noErr) then
  772.                     goto 1;
  773.                 disposeItem := true;        { dispose of hItem when done }
  774.  
  775. { identify the requested flavor type as either 'TEXT', 'styl' or 'SOUP' }
  776.                 if (theType = kTypeText) then
  777.                     err := WECopyRange(selStart, selEnd, hItem, nil, nil, hWE)
  778.                 else if (theType = kTypeStyles) then
  779.                     err := WECopyRange(selStart, selEnd, nil, hItem, nil, hWE)
  780.                 else if (theType = kTypeSoup) then
  781.                     err := WECopyRange(selStart, selEnd, nil, nil, hItem, hWE)
  782.                 else
  783.                     err := badDragFlavorErr;
  784.  
  785.                 if (err <> noErr) then
  786.                     goto 1;
  787.  
  788.             end;
  789.  
  790. { set the drag flavor data }
  791.         HLock(hItem);
  792.         err := SetDragItemFlavorData(theDrag, ItemReference(hWE), theType, hItem^, GetHandleSize(hItem), 0);
  793.         HUnlock(hItem);
  794.  
  795. 1:
  796. { return result code }
  797.         _WESendFlavor := err;
  798.  
  799. { clean up }
  800.         if (disposeItem) then
  801.             _WEForgetHandle(hItem);
  802.  
  803.     end;  { _WESendFlavor }
  804.  
  805.     function WEDraggedToTrash (theDrag: DragReference): Boolean;
  806.  
  807. { return TRUE if the drop location of the specified drag is the trash }
  808.  
  809.         label
  810.             1;
  811.         const
  812.             bDirectoryAttr = 4;
  813.         var
  814.             dropLocation, coercedDropLocation: AEDesc;
  815.             pb: CInfoPBRec;
  816.             pSpec: FSSpecPtr;
  817.             trashVRefNum: Integer;
  818.             trashDirID: LongInt;
  819.     begin
  820.         WEDraggedToTrash := false;
  821.         dropLocation.dataHandle := nil;
  822.         coercedDropLocation.dataHandle := nil;
  823.  
  824. { get drop location }
  825.         if (GetDropLocation(theDrag, dropLocation) <> noErr) then
  826.             goto 1;
  827.  
  828. { do nothing if dropLocation is a null descriptor }
  829.         if (dropLocation.descriptorType = typeNull) then
  830.             goto 1;
  831.  
  832. { try to coerce the descriptor to a file system specification record }
  833.         if (AECoerceDesc(dropLocation, typeFSS, coercedDropLocation) <> noErr) then
  834.             goto 1;
  835.  
  836. { lock the data handle of the coerced descriptor }
  837.         HLock(coercedDropLocation.dataHandle);
  838.         pSpec := FSSpecHandle(coercedDropLocation.dataHandle)^;
  839.  
  840. { determine the directory ID of the drop location (assuming it's a folder!) }
  841.         _WEBlockClr(@pb, SizeOf(pb));
  842.         pb.ioVRefNum := pSpec^.vRefNum;
  843.         pb.ioDirID := pSpec^.parID;
  844.         pb.ioNamePtr := @pSpec^.name;
  845.         if (PBGetCatInfoSync(@pb) <> noErr) then
  846.             goto 1;
  847.  
  848. { make sure the specified file system object is really a directory }
  849.         if (not BTST(pb.ioFlAttrib, bDirectoryAttr)) then
  850.             goto 1;
  851.  
  852. { find the directory ID of the trash folder }
  853.         if (FindFolder(pSpec^.vRefNum, kTrashFolderType, kDontCreateFolder, trashVRefNum, trashDirID) <> noErr) then
  854.             goto 1;
  855.  
  856. { compare the two directory IDs: if they're the same, the drop location is the trash }
  857.         if (pb.ioDrDirID = trashDirID) then
  858.             WEDraggedToTrash := true;
  859.  
  860. 1:
  861. { clean up }
  862.         if (AEDisposeDesc(dropLocation) <> noErr) then
  863.             ;
  864.         if (AEDisposeDesc(coercedDropLocation) <> noErr) then
  865.             ;
  866.  
  867.     end;  { WEDraggedToTrash }
  868.  
  869.     function _WEDrag (mouseLoc: Point;
  870.                                     modifiers: Integer;
  871.                                     clickTime: LongInt;
  872.                                     hWE: WEHandle): OSErr;
  873.         label
  874.             1;
  875.         var
  876.             pWE: WEPtr;
  877.             theDrag: DragReference;
  878.             hObjectDesc: WEObjectDescHandle;
  879.             theEvent: EventRecord;
  880.             dragRgn, tmpRgn: RgnHandle;
  881.             dragBounds: Rect;
  882.             portDelta: Point;
  883.             savePort: GrafPtr;
  884.             err: OSErr;
  885.     begin
  886.         theDrag := kNullDrag;
  887.         dragRgn := nil;
  888.         tmpRgn := nil;
  889.         pWE := hWE^;
  890.  
  891. { set up the port }
  892.         GetPort(savePort);
  893.         SetPort(pWE^.port);
  894.  
  895. { fabricate an EventRecord for TrackDrag }
  896.         theEvent.what := mouseDown;
  897.         theEvent.message := 0;
  898.         theEvent.when := clickTime;
  899.         theEvent.where := mouseLoc;
  900.         LocalToGlobal(theEvent.where);
  901.         theEvent.modifiers := modifiers;
  902.  
  903. { before seeing the dotted outline, the user must move the mouse a certain }
  904. { distance away from the initial mouse location; this allows a short click in the selection }
  905. { area to set the insertion point instead of starting a drag-and-drop sequence }
  906.         err := noDragErr;
  907.         if (WaitMouseMoved(theEvent.where) = false) then
  908.             goto 1;
  909.  
  910. { create a drag object }
  911.         err := NewDrag(theDrag);
  912.         if (err <> noErr) then
  913.             goto 1;
  914.  
  915. {$IFC WASTE_DEBUG}
  916.         _WEAssert(theDrag <> kNullDrag, 'Zero is a valid drag reference (??)');
  917. {$ENDC}
  918.  
  919. { if the selection range consists of an embedded object, }
  920. { then use its object type as flavor type }
  921.         if (WEGetSelectedObject(hObjectDesc, hWE) = noErr) then
  922.             begin
  923.                 err := AddDragItemFlavor(theDrag, ItemReference(hWE), hObjectDesc^^.objectType, nil, 0, 0);
  924.                 if (err <> noErr) then
  925.                     goto 1;
  926.             end
  927.         else
  928.             begin
  929.  
  930. { add a 'TEXT' flavor to the drag }
  931.                 err := AddDragItemFlavor(theDrag, ItemReference(hWE), kTypeText, nil, 0, 0);
  932.                 if (err <> noErr) then
  933.                     goto 1;
  934.  
  935. { add a 'styl' flavor to the drag }
  936.                 err := AddDragItemFlavor(theDrag, ItemReference(hWE), kTypeStyles, nil, 0, 0);
  937.                 if (err <> noErr) then
  938.                     goto 1;
  939.  
  940. { add a 'SOUP' flavor to the drag }
  941.                 err := AddDragItemFlavor(theDrag, ItemReference(hWE), kTypeSoup, nil, 0, 0);
  942.                 if (err <> noErr) then
  943.                     goto 1;
  944.  
  945.             end;
  946.  
  947. { since we didn't provide the flavor data for any of the above flavors, }
  948. { we need supply a data send callback }
  949.         if (_weFlavorSender = nil) then
  950.             _weFlavorSender := NewDragSendDataProc(@_WESendFlavor);
  951.         err := SetDragSendProc(theDrag, _weFlavorSender, nil);
  952.         if (err <> noErr) then
  953.             goto 1;
  954.  
  955. { get hilite region }
  956.         dragRgn := WEGetHiliteRgn(pWE^.selStart, pWE^.selEnd, hWE);
  957.  
  958. { we need just the outline of this region }
  959.         tmpRgn := NewRgn;
  960.         CopyRgn(dragRgn, tmpRgn);
  961.         InsetRgn(tmpRgn, 1, 1);
  962.         DiffRgn(dragRgn, tmpRgn, dragRgn);
  963.         DisposeRgn(tmpRgn);
  964.  
  965. { and we need it in global coordinates }
  966.         portDelta := Point(0);
  967.         LocalToGlobal(portDelta);
  968.         OffsetRgn(dragRgn, portDelta.h, portDelta.v);
  969.  
  970. { set the bounds of the drag }
  971.         dragBounds := dragRgn^^.rgnBBox;
  972.         err := SetDragItemBounds(theDrag, ItemReference(hWE), dragBounds);
  973.         if (err <> noErr) then
  974.             goto 1;
  975.  
  976. { stash drag reference in currentDrag so WETrackDrag and WEReceiveDrag }
  977. { can tell whether a given drag originated from this WE instance }
  978.         pWE^.currentDrag := theDrag;
  979.  
  980. { track the drag }
  981.         err := TrackDrag(theDrag, theEvent, dragRgn);
  982.         pWE^.currentDrag := kNullDrag;
  983.         if (err <> noErr) then
  984.             goto 1;
  985.  
  986. { if the selection was dragged to the trash and the option key wasn't held down }
  987. { and if the instance is editable, delete the selection }
  988.         if (not BTST(pWE^.flags, weFReadOnly)) then
  989.             if (WEDraggedToTrash(theDrag)) then
  990.                 if (_WEIsOptionDrag(theDrag) = false) then
  991.                     begin
  992.                         err := WEDelete(hWE);
  993.                         if (err <> noErr) then
  994.                             goto 1;
  995.                     end;
  996.  
  997. { clear result code }
  998.         err := noErr;
  999.  
  1000. 1:
  1001. { return result code }
  1002.         _WEDrag := err;
  1003.  
  1004. { dispose of the drag }
  1005.         if (theDrag <> kNullDrag) then
  1006.             if (DisposeDrag(theDrag) <> noErr) then
  1007.                 ;
  1008.  
  1009. { dispose of the drag region }
  1010.         if (dragRgn <> nil) then
  1011.             DisposeRgn(dragRgn);
  1012.  
  1013. { restore the port }
  1014.         SetPort(savePort);
  1015.  
  1016.     end;  { _WEDrag }
  1017.  
  1018. {$IFC WASTE_SEGMENT}
  1019. {$S}
  1020. {$ENDC}
  1021.  
  1022.     procedure WEClick (mouseLoc: Point;
  1023.                                     modifiers: Integer;
  1024.                                     clickTime: LongInt;
  1025.                                     hWE: WEHandle);
  1026.         label
  1027.             1;
  1028.         var
  1029.             pWE: WEPtr;
  1030.             hObjectDesc: WEObjectDescHandle;
  1031.             thePoint: LongPt;
  1032.             offset, anchor: LongInt;
  1033.             rangeStart, rangeEnd: LongInt;
  1034.             edge: SignedByte;
  1035.             isMultipleClick: Boolean;
  1036.             saveWELock: Boolean;
  1037.     begin
  1038.  
  1039. { stop any ongoing inline input session }
  1040.         WEStopInlineSession(hWE);
  1041.  
  1042. { lock the WE record }
  1043.         saveWELock := _WESetHandleLock(hWE, true);
  1044.         pWE := hWE^;
  1045.  
  1046. { hide the caret if it's showing }
  1047.         if BTST(pWE^.flags, weFCaretVisible) then
  1048.             _WEBlinkCaret(hWE);
  1049.  
  1050. { find click offset }
  1051.         WEPointToLongPoint(mouseLoc, thePoint);
  1052.         offset := WEGetOffset(thePoint, edge, hWE);
  1053.  
  1054. { determine whether this click is part of a sequence }
  1055. { a single click inside an object selects it, so it's like a double click in a word }
  1056.         isMultipleClick := ((clickTime < pWE^.clickTime + GetDblTime) and (offset = pWE^.clickLoc));
  1057.  
  1058. { remember click time, click offset and edge value }
  1059.         pWE^.clickTime := clickTime;
  1060.         pWE^.clickLoc := offset;
  1061.         pWE^.clickEdge := edge;
  1062.  
  1063. { when selected, embedded objects can intercept mouse clicks }
  1064.         if (WEGetSelectedObject(hObjectDesc, hWE) = noErr) then
  1065.             if _WEOffsetInRange(offset, edge, pWE^.selStart, pWE^.selEnd) then
  1066.                 if (_WEClickObject(mouseLoc, modifiers + ORD(isMultipleClick), clickTime, hObjectDesc)) then
  1067.                     goto 1;
  1068.  
  1069.         if (BAND(modifiers, shiftKey) = 0) then
  1070.             begin
  1071.  
  1072. { is this click part of a sequence or is it a single click? }
  1073.                 if (isMultipleClick) then
  1074.                     begin
  1075.                         pWE^.clickCount := pWE^.clickCount + 1;
  1076.  
  1077. { a double (triple) click creates an anchor-word (anchor-line) }
  1078.                         if (pWE^.clickCount > 1) then
  1079.                             WEFindLine(offset, edge, pWE^.anchorStart, pWE^.anchorEnd, hWE)
  1080.                         else
  1081.                             WEFindWord(offset, edge, pWE^.anchorStart, pWE^.anchorEnd, hWE);
  1082.  
  1083.                         offset := pWE^.anchorStart;
  1084.  
  1085.                     end
  1086.                 else
  1087.                     begin
  1088.  
  1089. { single-click }
  1090. { if the Drag Manager is available and the click went in the selection range, }
  1091. { this click may be the beginning of a drag gesture }
  1092.                         if (BTST(pWE^.flags, weFHasDragManager) and BTST(pWE^.flags, weFDragAndDrop)) then
  1093.                             if _WEOffsetInRange(offset, edge, pWE^.selStart, pWE^.selEnd) then
  1094.                                 if (_WEDrag(mouseLoc, modifiers, clickTime, hWE) <> noDragErr) then
  1095.                                     goto 1;
  1096.  
  1097.                         pWE^.clickCount := 0;
  1098.                         anchor := offset;
  1099.                     end
  1100.             end
  1101.         else
  1102.  
  1103. { if the shift key was down, use the old anchor offset found with the previous click }
  1104.             if BTST(pWE^.flags, weFAnchorIsEnd) then
  1105.                 anchor := pWE^.selEnd
  1106.             else
  1107.                 anchor := pWE^.selStart;
  1108.  
  1109. { set the weFMouseTracking bit while we track the mouse }
  1110.         BSET(pWE^.flags, weFMouseTracking);
  1111.  
  1112. { MOUSE TRACKING LOOP }
  1113.         repeat
  1114.  
  1115. { get text offset corresponding to mouse position }
  1116.             WEPointToLongPoint(mouseLoc, thePoint);
  1117.             offset := WEGetOffset(thePoint, edge, hWE);
  1118.  
  1119. { if we're selecting words or lines, pin offset to a word or line boundary }
  1120.             if (pWE^.clickCount > 0) then
  1121.                 begin
  1122.                     if (pWE^.clickCount > 1) then
  1123.                         WEFindLine(offset, edge, rangeStart, rangeEnd, hWE)
  1124.                     else
  1125.                         WEFindWord(offset, edge, rangeStart, rangeEnd, hWE);
  1126.  
  1127. { choose the word/line boundary and the anchor that are farthest away from each other }
  1128.                     if (offset > pWE^.anchorStart) then
  1129.                         begin
  1130.                             anchor := pWE^.anchorStart;
  1131.                             offset := rangeEnd;
  1132.                         end
  1133.                     else
  1134.                         begin
  1135.                             offset := rangeStart;
  1136.                             anchor := pWE^.anchorEnd;
  1137.                         end;
  1138.                 end
  1139.             else
  1140.  
  1141. { if the point is in the middle of an object, the selection should include it }
  1142.                 if (edge = kObjectEdge) then
  1143.                     offset := offset + 1;
  1144.  
  1145. { set the selection range from anchor point to current offset }
  1146.             WESetSelection(anchor, offset, hWE);
  1147.  
  1148. { call the click loop callback, if any }
  1149.             if (pWE^.clickLoop <> nil) then
  1150.                 if (CallWEClickLoopProc(hWE, pWE^.clickLoop) = false) then
  1151.                     Leave;
  1152.  
  1153. { update mouse position }
  1154.             GetMouse(mouseLoc);
  1155.  
  1156.         until (not WaitMouseUp);
  1157.  
  1158. { clear the weFMouseTracking bit }
  1159.         BCLR(pWE^.flags, weFMouseTracking);
  1160.  
  1161. { redraw the caret immediately if the selection range is empty }
  1162.         if (anchor = offset) then
  1163.             _WEBlinkCaret(hWE);
  1164.  
  1165. 1:
  1166. { unlock the WE record }
  1167.         if (_WESetHandleLock(hWE, saveWELock)) then
  1168.             ;
  1169.  
  1170.     end;  { WEClick }
  1171.  
  1172. end.